package com.zhu.tool_test.util;


import com.hitotek.util.DateUtil;
import com.hitotek.util.TopDateUtil;
import core.Data;
import core.MultipartData;
import util.DateUtils;
import weapon.EntityWeapons;

import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.text.ParseException;
import java.util.*;

/**
 * @program: smartpark-service-ep
 * @description:
 * @author: ggBall
 * @create: 2020-11-11 20:29
 **/
public class EpMultipartData extends MultipartData {
    private static Set<Class<?>> BASIC_WRAPPED_TYPES;
    static {
        BASIC_WRAPPED_TYPES = new HashSet<>(8);
        BASIC_WRAPPED_TYPES.add(Byte.class);
        BASIC_WRAPPED_TYPES.add(Short.class);
        BASIC_WRAPPED_TYPES.add(Integer.class);
        BASIC_WRAPPED_TYPES.add(Long.class);
        BASIC_WRAPPED_TYPES.add(Float.class);
        BASIC_WRAPPED_TYPES.add(Double.class);
        BASIC_WRAPPED_TYPES.add(Boolean.class);
        BASIC_WRAPPED_TYPES.add(Character.class);
        BASIC_WRAPPED_TYPES.add(Date.class);
    }

    @Override
    public <T> List<T> getListPart(String key, Class<T> tClass, boolean deep) {
        if (isPresent(key)) {
            Object o = get(key);
            if (null == o) {
                return null;
            }
            try {
                if (List.class.isAssignableFrom(o.getClass())) {
                    List list = (List) o;
                    if (list.size() > 0) {
                        Object firstItem = list.get(0);
                        Class<?> listClazz = firstItem.getClass();
                        if (Object.class.equals(listClazz)) {
                            //noinspection unchecked
                            return list;
                        }
                        if (tClass.equals(listClazz)) {
                            //noinspection unchecked
                            return (List<T>) o;
                        }
                        // if deep conversion is turned on,
                        // need to continue to judge the type
                        if (deep) {
                            if (Map.class.isAssignableFrom(listClazz)) {
                                List<T> res = new ArrayList<>();
                                for (Object item : list) {
                                    EpMultipartData data;
                                    if (Data.class.isAssignableFrom(listClazz)) {
                                        data = (EpMultipartData) item;
                                    } else {
                                        //noinspection unchecked
                                        data = new EpMultipartData();
                                        data.putAll((Map<String, Object>) item);
                                    }
                                    T t = data.get(tClass, true);
                                    res.add(t);
                                }

                                return res;
                            }
                        }
                    } else {
                        // noinspection unchecked
                        return list;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
        return null;
    }



    @Override
    public <T> T get(Class<T> tClass, boolean deep) {
        Optional<T> instance = EntityWeapons.instance(tClass);
        if (instance.isPresent()) {
            Field[] fields = EntityWeapons.getAllDeclaredFields(tClass);
            T target = instance.get();
            for (Field field : fields) {
                parseField(field, target, deep);
            }
            return target;
        }
        return null;
    }


    private <T> void parseField(Field field, T target, boolean deep) {
        field.setAccessible(true);
        // non collection object parsing injection
        Class<?> type = field.getType();
        String fieldName = field.getName();
        if (!List.class.isAssignableFrom(type)) {

            if (!this.isPresent(fieldName)) {
                return;
            }
            try {
                field.set(target, getPart(fieldName, type, deep));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } else {
            try {
                field.set(target, getListPart(fieldName, EntityWeapons.getListGenericType(field), deep));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        field.setAccessible(false);
    }


    @Override
    public <T> T getPart(String key, Class<T> tClass, boolean deep) {
        if (BASIC_WRAPPED_TYPES.contains(tClass)) {
            return this.dispatch(key, tClass);
        }
        Object o = get(key);
        if (null == o) {
            return null;
        }
        if (tClass.isAssignableFrom(o.getClass())) {
            return tClass.cast(o);
        }
        return deep ? this.deepTrans(tClass, key) : null;
    }
    private <T> T deepTrans(Class<T> tClass, String key) {
        Object o = get(key);
        if (null == o) {
            return null;
        }
        Class<?> clazz = o.getClass();
        if (!List.class.isAssignableFrom(clazz)) {
            Data temp = null;
            if (Data.class.isAssignableFrom(clazz)) {
                temp = (Data) o;
            } else if (Map.class.isAssignableFrom(clazz)) {
                // noinspection unchecked
                temp = new Data();
                temp.putAll((Map<String, Object>) o);
            }
            T res = null == temp ? null : temp.get(tClass, true);

            return res;
        }
        return null;
    }

    // 将map 装入新的 EpMultipartData
    public  EpMultipartData fusion (Map map){


        Set set = map.keySet();

        Iterator iterator = set.iterator();

        while (iterator.hasNext()){
            Object next = iterator.next();
            // 键不为String 则不存
            if(!(next instanceof String)){
                continue;
            }
            Optional.ofNullable(next).ifPresent(key -> this.include(key.toString(),map.get(key)));
        }

        return this;
    }



    @Override
    public Date getDatePart(String key) {
        Object o = this.get(key);
        boolean isLong = false;
        if (o instanceof Long) {
            isLong = true;
        }

        if (o instanceof Timestamp) {
            Timestamp timestamp = (Timestamp)o;
            return DateUtils.timestamp2Date(timestamp.getTime() / 1000L);
        } else if (isLong) {
            Long ts = null;
            try {
                 ts = Long.valueOf(o.toString());
            } catch (NumberFormatException var6) {
                var6.printStackTrace();
            }
            if (13 == ts.toString().length()) {
                return new Date(ts);
            } else if (10 == ts.toString().length()) {
                return new Date(ts * 10L);
            } else {
                throw new RuntimeException("Unexpected time! key name is '" + key + "', value is '" + o + "'");
            }
        } else if (o instanceof String) {
            String str = String.valueOf(o);
            Date date = null;
            try {
                 date = TopDateUtil.transferStrToDate(str, DateUtil.YYYY_MM_DD_HH_MM_SS);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return date;
        } else {
            return o instanceof String ? DateUtils.from(this.getStringPart(key)) : (Date)this.getPart(key, Date.class);
        }
    }

    private <T> T dispatch(String key, Class<T> tClass) {
        if (Byte.class.isAssignableFrom(tClass)) {
            return tClass.cast(this.getBytePart(key));
        }
        if (Integer.class.isAssignableFrom(tClass)) {
            return tClass.cast(this.getIntegerPart(key));
        }
        if (Long.class.isAssignableFrom(tClass)) {
            return tClass.cast(this.getLongPart(key));
        }
        if (Float.class.isAssignableFrom(tClass)) {
            return tClass.cast(this.getFloatPart(key));
        }
        if (Double.class.isAssignableFrom(tClass)) {
            return tClass.cast(this.getDoublePart(key));
        }
        if (Boolean.class.isAssignableFrom(tClass)) {
            return tClass.cast(this.getBooleanPart(key));
        }
        if (Character.class.isAssignableFrom(tClass)) {
            return tClass.cast(this.getCharPart(key));
        }
        if (Date.class.isAssignableFrom(tClass)) {
            String stringPart = this.getStringPart(key);

            try {
                Date date = TopDateUtil.transferStrToDate(stringPart, DateUtil.YYYY_MM_DD_HH_MM_SS);
                return tClass.cast(date);
            } catch (ParseException e) {
                e.printStackTrace();
            }


        }
        return null;
    }







}
